﻿package fab.geom
{
	import flash.geom.Point;
	/**
	 * Equation cartesienne ax + by + c = 0
	 * 
	 * @example
 	 * <pre class="prettyprint">
		public var p1:Point = new Point( 50, 100);
		public var p2:Point = new Point( 500, 400);
		public var line1:Line = new Line( p1, p2 ) ;
		public var line2:Line = new Line( p1, p2, "vecteur" ) ;
		Drawer.drawLine(this, line1);
	 * </pre>
	 */
	public class Line
	{
		public var a:Number;
		public var b:Number;
		public var c:Number;
		private var _coeff:Number=0;
		private var _pente:Number=0;
		private var _directeur:Point;
		private var _normale:Point;

		public function Line( p1:*, p2:*=null, method:String="points", normee:Boolean=false )
		{
			switch( method )
			{
				//case "polaire":
					
				//break;						
				case "horizontale":
					a = 0;
					b = 1;
					c = - p1;
				break;					
				case "equation":// y = param1 * x + param2;
					a = - p1;
					b = 1;
					c = - p2;				
				break;	
				
				case "vecteur":
				p2 = p1.add( p2 );
				update( p1, p2 );
				break;
				
				case "polaire":
				p2 = (p1 as Point).add( _directeur = new Point( Math.cos( p2 ), Math.sin( p2 ) ) );
				update( p1, p2 );
				break;
				
				case "points":
				default:
				if ( p1 is Point && p2 is Point ) update( p1, p2 );
			}
			if ( normee ) normalize();
		}
		public function update( p1:Point , p2:Point ):void
		{				
			var x0:Number = p1.x;
			var y0:Number = p1.y;
			var x1:Number = p2.x;
			var y1:Number = p2.y;
			
			if (x0 == x1) // cas particulier
			{
				if (y0 == y1)// points identiques
				{
				}
				else // verticale
				{
					a = 1;
					b = 0;
					c = - x0;
				}
			}
			else 
			{
				a = ( y1 - y0 ) / ( x0 - x1 );
				b = 1;
				c = - y0 - a * x0;
			}
		}
		public function normalize():void
		{
			a /= coeff;
			b /= coeff;
			c /= coeff;
		}	
		public function isVertical():Boolean
		{
			return b == 0;
		}			
		public function getY( x:Number ):Number
		{
			return ( - a * x - c ) / b;
		}			
		public function projection( p:Point ):Point
		{
			return intersection( this, new Line( p, normale, "vecteur" ) );
		}	
		public function distance( p:Point ):Number
		{
			return Math.abs( a * p.x + b * p.y + c ) / coeff;
		}
		public function where( p:Point ):Number
		{
			return a * p.x + b * p.y + c;
		}
		/*
		 * retourne le coefficient directeur de la ligne
		 * */				
		private function get coeff():Number
		{
			if ( _coeff != 0 ) return _coeff;
			else return _coeff = Math.sqrt( Math.pow( a, 2 ) + Math.pow( b, 2 ) );
		}
		/*
		 * retourne la pente de la ligne
		 * */			
		private function get pente():Number
		{
			if ( _pente != 0 ) return _pente;
			else return _pente = b==0?Number.POSITIVE_INFINITY:-a/b;
		}
		/*
		 * retourne le vecteur directeur de la ligne
		 * */			
		public function get directeur():Point
		{
			if ( _directeur != null ) return _directeur;
			_directeur = new Point( b, -a );
			_directeur.normalize(1);
			return _directeur;
		}
		/*
		 * retourne le vecteur normal à la ligne
		 * */	
		public function get normale():Point
		{
			if ( _normale != null ) return _normale;
			_normale = new Point( a, b );
			_normale.normalize(1);			
			return _normale;
		}
		/*
		 * retourne true si le point appartient exactement à la ligne
		 * */		
		public function isOnLine( p:Point ):Boolean// use approximation ?
		{
			return Math.abs( a * p.x + b * p.y + c ) == 0;
		}
		
		/*
		 * retourne le denominateur entre 2 lignes
		 * */
		static public function denominator(line1:Line, line2:Line):Number
		{
			return line1.a * line2.b - line2.a * line1.b;
		}
		/*
		 * retourne le Point d'intersection entre 2 lignes
		 * */		
		static public function intersection(line1:Line, line2:Line):Point
		{
			var _d:Number = denominator( line1, line2 );
			if ( _d == 0 ) return null;
			var p:Point = new Point();
			
			//p.x = Math.round( ( line1.b * line2.c - line2.b * line1.c ) / _d );
			p.x = ( line1.b * line2.c - line2.b * line1.c ) / _d;
			
			var y:Number = line1.getY( p.x );
			if ( isNaN( y ) ) y = line2.getY( p.x );
			//p.y = Math.round( y );
			p.y = y;
			return p;
		}
		/*
		 * retourne true si les 2 lignes sont perpendiculaires
		 * */		
		static public function ortho(line1:Line, line2:Line):Boolean
		{
			return line1.pente * line2.pente == -1;
		}
		
		public function toString():String
		{
			return "Ligne " + a + " / " + b + " / " + c;
		}			
	}
}